import os
import pathlib
import shutil
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import yaml
import ctypes
from ctypes import wintypes

class MultiFolderCopyGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("多文件夹批量复制工具")
        self.root.geometry("900x650")
        self.root.minsize(800, 600)
        
        # 存储选择的路径
        self.source_files = []
        self.source_folders = []
        self.target_folders = []
        
        # 创建界面
        self.create_widgets()
        
        # 初始化路径
        self.current_path = str(pathlib.Path(__file__).parent.absolute().resolve())

    def create_widgets(self):
        # 主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 源文件选择区域
        ttk.Label(main_frame, text="1. 选择需要复制的文件 (可多选):", font=('微软雅黑', 10, 'bold')).pack(anchor=tk.W, pady=(10, 5))
        file_frame = ttk.Frame(main_frame)
        file_frame.pack(fill=tk.X, pady=(0, 5))
        
        ttk.Button(file_frame, text="添加文件...", command=self.add_source_files).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(file_frame, text="清除全部", command=self.clear_source_files).pack(side=tk.LEFT)
        
        # 源文件列表
        file_list_frame = ttk.Frame(main_frame)
        file_list_frame.pack(fill=tk.X, pady=(0, 10))
        
        self.file_listbox = tk.Listbox(file_list_frame, height=3, selectmode=tk.EXTENDED)
        self.file_listbox.pack(side=tk.LEFT, fill=tk.X, expand=True)
        
        file_scroll = ttk.Scrollbar(file_list_frame, orient=tk.VERTICAL, command=self.file_listbox.yview)
        file_scroll.pack(side=tk.RIGHT, fill=tk.Y)
        self.file_listbox.config(yscrollcommand=file_scroll.set)
        
        ttk.Button(main_frame, text="删除选中文件", command=self.remove_selected_files).pack(anchor=tk.W, pady=(0, 10))
        
        # 源文件夹选择区域
        ttk.Label(main_frame, text="2. 选择需要复制的文件夹 (可一次性多选):", font=('微软雅黑', 10, 'bold')).pack(anchor=tk.W, pady=(10, 5))
        folder_frame = ttk.Frame(main_frame)
        folder_frame.pack(fill=tk.X, pady=(0, 5))
        
        ttk.Button(folder_frame, text="添加文件夹...", command=self.add_source_folders).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(folder_frame, text="清除全部", command=self.clear_source_folders).pack(side=tk.LEFT)
        
        # 源文件夹列表
        folder_list_frame = ttk.Frame(main_frame)
        folder_list_frame.pack(fill=tk.X, pady=(0, 10))
        
        self.folder_listbox = tk.Listbox(folder_list_frame, height=3, selectmode=tk.EXTENDED)
        self.folder_listbox.pack(side=tk.LEFT, fill=tk.X, expand=True)
        
        folder_scroll = ttk.Scrollbar(folder_list_frame, orient=tk.VERTICAL, command=self.folder_listbox.yview)
        folder_scroll.pack(side=tk.RIGHT, fill=tk.Y)
        self.folder_listbox.config(yscrollcommand=folder_scroll.set)
        
        ttk.Button(main_frame, text="删除选中文件夹", command=self.remove_selected_folders).pack(anchor=tk.W, pady=(0, 10))
        
        # 目标文件夹选择区域
        ttk.Label(main_frame, text="3. 选择目标文件夹 (可一次性多选):", font=('微软雅黑', 10, 'bold')).pack(anchor=tk.W, pady=(10, 5))
        target_frame = ttk.Frame(main_frame)
        target_frame.pack(fill=tk.X, pady=(0, 5))
        
        ttk.Button(target_frame, text="添加目标文件夹...", command=self.add_target_folders).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(target_frame, text="清除全部", command=self.clear_target_folders).pack(side=tk.LEFT)
        
        # 目标文件夹列表
        target_list_frame = ttk.Frame(main_frame)
        target_list_frame.pack(fill=tk.X, pady=(0, 10))
        
        self.target_listbox = tk.Listbox(target_list_frame, height=3, selectmode=tk.EXTENDED)
        self.target_listbox.pack(side=tk.LEFT, fill=tk.X, expand=True)
        
        target_scroll = ttk.Scrollbar(target_list_frame, orient=tk.VERTICAL, command=self.target_listbox.yview)
        target_scroll.pack(side=tk.RIGHT, fill=tk.Y)
        self.target_listbox.config(yscrollcommand=target_scroll.set)
        
        ttk.Button(main_frame, text="删除选中目标文件夹", command=self.remove_selected_targets).pack(anchor=tk.W, pady=(0, 10))
        
        # 操作按钮
        btn_frame = ttk.Frame(main_frame)
        btn_frame.pack(fill=tk.X, pady=(10, 10))
        
        ttk.Button(btn_frame, text="开始复制", command=self.start_copy, style='Accent.TButton').pack(side=tk.LEFT, padx=(0, 10))
        ttk.Button(btn_frame, text="清除日志", command=self.clear_log).pack(side=tk.LEFT)
        
        # 进度条
        self.progress_var = tk.DoubleVar()
        ttk.Label(main_frame, text="复制进度:").pack(anchor=tk.W, pady=(5, 2))
        self.progress_bar = ttk.Progressbar(main_frame, variable=self.progress_var, maximum=100)
        self.progress_bar.pack(fill=tk.X, pady=(0, 10))
        
        # 日志区域
        ttk.Label(main_frame, text="操作日志:").pack(anchor=tk.W, pady=(5, 2))
        log_frame = ttk.Frame(main_frame)
        log_frame.pack(fill=tk.BOTH, expand=True)
        
        log_scroll = ttk.Scrollbar(log_frame)
        log_scroll.pack(side=tk.RIGHT, fill=tk.Y)
        
        self.log_text = tk.Text(log_frame, wrap=tk.WORD, yscrollcommand=log_scroll.set, state=tk.DISABLED, height=8)
        self.log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        log_scroll.config(command=self.log_text.yview)
        
        # 设置样式
        style = ttk.Style()
        style.configure('Accent.TButton', font=('微软雅黑', 9, 'bold'))

    # 多文件夹选择对话框（改进版）
    def select_multiple_folders(self, title):
        # 对于Windows系统，使用更可靠的多文件夹选择实现
        if os.name == 'nt':
            # 尝试使用Windows API实现真正的多文件夹选择
            try:
                # 初始化COM库
                ctypes.windll.ole32.CoInitialize(None)
                
                # 定义结构体和常量
                class OPENFILENAME(ctypes.Structure):
                    _fields_ = [
                        ("lStructSize", wintypes.DWORD),
                        ("hwndOwner", wintypes.HWND),
                        ("hInstance", wintypes.HINSTANCE),
                        ("lpstrFilter", wintypes.LPCWSTR),
                        ("lpstrCustomFilter", wintypes.LPWSTR),
                        ("nMaxCustFilter", wintypes.DWORD),
                        ("nFilterIndex", wintypes.DWORD),
                        ("lpstrFile", wintypes.LPWSTR),
                        ("nMaxFile", wintypes.DWORD),
                        ("lpstrFileTitle", wintypes.LPWSTR),
                        ("nMaxFileTitle", wintypes.DWORD),
                        ("lpstrInitialDir", wintypes.LPCWSTR),
                        ("lpstrTitle", wintypes.LPCWSTR),
                        ("Flags", wintypes.DWORD),
                        ("nFileOffset", wintypes.WORD),
                        ("nFileExtension", wintypes.WORD),
                        ("lpstrDefExt", wintypes.LPCWSTR),
                        ("lCustData", wintypes.LPARAM),
                        ("lpfnHook", ctypes.CFUNCTYPE(wintypes.UINT, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM)),
                        ("lpTemplateName", wintypes.LPCWSTR),
                        ("pvReserved", ctypes.c_void_p),
                        ("dwReserved", wintypes.DWORD),
                        ("FlagsEx", wintypes.DWORD)
                    ]
                
                # 缓冲区大小，确保能容纳多个路径
                buffer_size = 1024 * 1024  # 1MB缓冲区
                buffer = ctypes.create_unicode_buffer(buffer_size)
                
                # 初始化结构体
                ofn = OPENFILENAME()
                ofn.lStructSize = ctypes.sizeof(ofn)
                ofn.hwndOwner = self.root.winfo_id()
                ofn.lpstrFilter = "所有文件夹\0*.*\0\0"
                ofn.lpstrFile = buffer
                ofn.nMaxFile = buffer_size
                ofn.lpstrTitle = title
                # 关键标志：允许多选、只选文件夹、路径必须存在
                ofn.Flags = 0x00080000 | 0x00000200 | 0x00000004  # OFN_ALLOWMULTISELECT | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST
                
                # 调用API显示对话框
                GetOpenFileName = ctypes.windll.comdlg32.GetOpenFileNameW
                if GetOpenFileName(ctypes.byref(ofn)):
                    # 处理返回的多个路径
                    paths = buffer.value.split('\x00')
                    paths = [p for p in paths if p]  # 过滤空字符串
                    
                    if not paths:
                        return []
                        
                    # 如果选择了多个文件/文件夹
                    if len(paths) > 1:
                        base_dir = paths[0]
                        return [os.path.join(base_dir, p) for p in paths[1:]]
                    else:
                        return [paths[0]]
                return []
                
            except Exception as e:
                self.log(f"使用API选择多文件夹失败: {str(e)}")
                # 失败时使用备选方案
                return self.fallback_folder_selector(title)
            finally:
                # 释放COM库
                ctypes.windll.ole32.CoUninitialize()
        else:
            # 非Windows系统使用备选方案
            return self.fallback_folder_selector(title)
    
    # 备选文件夹选择方案（确保可用性）
    def fallback_folder_selector(self, title):
        """当API调用失败时使用的备选文件夹选择方式"""
        from tkinter import simpledialog
        folders = []
        
        # 显示操作提示
        messagebox.showinfo("选择提示", "请在打开的对话框中依次选择文件夹，完成后点击取消")
        
        while True:
            folder = filedialog.askdirectory(title=title, initialdir=self.current_path)
            if not folder:  # 点击取消
                break
            if folder not in folders:
                folders.append(folder)
                # 显示已选择数量
                self.log(f"已选择 {len(folders)} 个文件夹")
        
        return folders

    # 源文件相关操作
    def add_source_files(self):
        files = filedialog.askopenfilenames(
            initialdir=self.current_path,
            title="选择要复制的文件"
        )
        if files:
            for file in files:
                if file not in self.source_files:
                    self.source_files.append(file)
                    self.file_listbox.insert(tk.END, file)

    def clear_source_files(self):
        self.source_files.clear()
        self.file_listbox.delete(0, tk.END)

    def remove_selected_files(self):
        selected = sorted(self.file_listbox.curselection(), reverse=True)
        for i in selected:
            del self.source_files[i]
            self.file_listbox.delete(i)

    # 源文件夹相关操作
    def add_source_folders(self):
        folders = self.select_multiple_folders("选择要复制的文件夹 (Windows: 按住Ctrl/Shift键多选)")
        if folders:
            for folder in folders:
                if folder and folder not in self.source_folders:
                    self.source_folders.append(folder)
                    self.folder_listbox.insert(tk.END, folder)

    def clear_source_folders(self):
        self.source_folders.clear()
        self.folder_listbox.delete(0, tk.END)

    def remove_selected_folders(self):
        selected = sorted(self.folder_listbox.curselection(), reverse=True)
        for i in selected:
            del self.source_folders[i]
            self.folder_listbox.delete(i)

    # 目标文件夹相关操作
    def add_target_folders(self):
        folders = self.select_multiple_folders("选择目标文件夹 (Windows: 按住Ctrl/Shift键多选)")
        if folders:
            for folder in folders:
                if folder and folder not in self.target_folders:
                    self.target_folders.append(folder)
                    self.target_listbox.insert(tk.END, folder)

    def clear_target_folders(self):
        self.target_folders.clear()
        self.target_listbox.delete(0, tk.END)

    def remove_selected_targets(self):
        selected = sorted(self.target_listbox.curselection(), reverse=True)
        for i in selected:
            del self.target_folders[i]
            self.target_listbox.delete(i)

    # 日志和进度条操作
    def log(self, message):
        self.log_text.config(state=tk.NORMAL)
        self.log_text.insert(tk.END, message + "\n")
        self.log_text.see(tk.END)
        self.log_text.config(state=tk.DISABLED)
        self.root.update_idletasks()

    def clear_log(self):
        self.log_text.config(state=tk.NORMAL)
        self.log_text.delete(1.0, tk.END)
        self.log_text.config(state=tk.DISABLED)

    # 核心复制功能
    def start_copy(self):
        # 验证选择
        if not self.source_files and not self.source_folders:
            messagebox.showerror("错误", "请至少选择一个文件或文件夹进行复制！")
            return
            
        if not self.target_folders:
            messagebox.showerror("错误", "请至少选择一个目标文件夹！")
            return
        
        # 准备所有要复制的项目
        items_to_copy = []
        items_to_copy.extend(self.source_files)
        items_to_copy.extend(self.source_folders)
        
        total_items = len(items_to_copy)
        total_targets = len(self.target_folders)
        total_operations = total_items * total_targets
        completed_operations = 0
        
        self.log("===== 开始复制操作 =====")
        self.log(f"待复制项: {total_items} 个（文件: {len(self.source_files)}, 文件夹: {len(self.source_folders)}）")
        self.log(f"目标文件夹: {total_targets} 个")
        
        for target_idx, target_folder in enumerate(self.target_folders, 1):
            self.log(f"\n处理目标文件夹 {target_idx}/{total_targets}: {target_folder}")
            
            for item_idx, item in enumerate(items_to_copy, 1):
                item_name = os.path.basename(item)
                target_path = os.path.join(target_folder, item_name)
                
                try:
                    # 确保目标目录存在
                    os.makedirs(os.path.dirname(target_path), exist_ok=True)
                    
                    if os.path.isfile(item):
                        # 处理YAML文件
                        if item_name.lower().endswith(('.yaml', '.yml')):
                            with open(item, 'r', encoding='utf-8') as f:
                                content = f.read()
                            with open(target_path, 'w', encoding='utf-8') as f:
                                f.write(content)
                            self.log(f"  YAML文件 {item_idx}/{total_items}: {item_name} [成功]")
                        else:
                            shutil.copy2(item, target_path)
                            self.log(f"  文件 {item_idx}/{total_items}: {item_name} [成功]")
                    elif os.path.isdir(item):
                        if not os.path.exists(target_path):
                            shutil.copytree(item, target_path)
                            self.log(f"  目录 {item_idx}/{total_items}: {item_name} [成功]")
                        else:
                            self.log(f"  目录 {item_idx}/{total_items}: {item_name} [已存在，跳过]")
                    else:
                        self.log(f"  未知类型 {item_idx}/{total_items}: {item_name} [跳过]")
                        
                except Exception as e:
                    self.log(f"  错误 {item_idx}/{total_items}: {item_name} → {str(e)}")
                
                # 更新进度
                completed_operations += 1
                progress = (completed_operations / total_operations) * 100
                self.progress_var.set(progress)
                self.root.update_idletasks()
        
        self.log("\n===== 操作完成 =====")
        messagebox.showinfo("完成", "所有复制操作已完成！")
        self.progress_var.set(100)

if __name__ == "__main__":
    root = tk.Tk()
    app = MultiFolderCopyGUI(root)
    root.mainloop()
    from PySide6.QtWidgets import QFileDialog
    QFileDialog.getOpenFileNames()